/**
 * 对象信息
 * @param {FileInfo|FreeFileInfo} fileInfo
 * @param {String} name
 * @param {Object} [obj]
 * @param {DocEntry} [doc]
 * @constructor
 */
function ObjectInfo(fileInfo, name, obj, doc)
{
	obj = obj || fileInfo.getObject(name);
	doc = doc || fileInfo.getDocEntry(name);

	if (obj == null)
	{
		if (doc)
		{
			var c = doc.getInstanceof()
				, t = doc.getTypeof();

			if (t == "function" || (t == null && c == "Function"))
			{
				return doc.isConstructor() ? new ConstructorInfo(fileInfo, name, obj, doc) : new FunctionInfo(fileInfo, name, obj, doc);
			}

			if (c || t == "object")
			{
				this.fileInfo = fileInfo;
				this.name = name;
				this.object = obj;
				this.docEntry = doc;
			}
			else if (t)
			{
				return new PrimitiveInfo(fileInfo, name, obj, doc);
			}
			else
			{
				return new UnknowInfo(fileInfo, name, obj, doc);
			}
		}
		else
		{
			return new UnknowInfo(fileInfo, name, obj, emptyDocEntry);
		}
	}
	else if (obj instanceof Function)
	{
		for (var x in obj.prototype)
		{
			return new ConstructorInfo(fileInfo, name, obj, doc || emptyDocEntry);
		}

		if (doc && doc.isConstructor())
		{
			return new ConstructorInfo(fileInfo, name, obj, doc);
		}
		return new FunctionInfo(fileInfo, name, obj, doc || emptyDocEntry);
	}
	else if (obj instanceof Object)
	{
		//some IE native function is object
		if (doc && doc.isConstructor())
		{
			return new ConstructorInfo(fileInfo, name, obj, doc);
		}
		this.fileInfo = fileInfo;
		this.name = name;
		this.object = obj;
		this.docEntry = doc || emptyDocEntry;
	}
	else
	{
		return new PrimitiveInfo(fileInfo, name, obj, doc || emptyDocEntry);
	}
}

$p = ObjectInfo.prototype;
/**
 * 返回说明信息
 * @return {String} 说明信息
 */
$p.getDescription = function()
{
	return this.docEntry.getDescription();
}
/**
 * 返回简介信息
 * @return {String} 简介信息
 */
$p.getShortDescription = function()
{
	if (!("_shortDescription" in this))
	{
		var sd = this.getDescription();

		if (sd)
		{
			this._shortDescription = sd.replace(/^([^\.\r\u3002$]+)[\s\S]+$/, "$1.")
		}
		else
		{
			this._shortDescription = "";
		}
	}
	return this._shortDescription;
}
/**
 * 返回类型信息
 * @return {String} 类型信息
 */
$p.getType = function()
{
	return (this.object ? this.object instanceof RegExp ? "object" : typeof this.object : this.docEntry.getTypeof() || "undefined");
}
/**
 * 返回构造器信息
 * @return {Object} 构造器信息
 */
$p.getConstructorInfo = function()
{
	if (!("_constructorInfo" in this))
	{
		var c = this.docEntry.getInstanceof();

		if (c)
		{
			this._constructorInfo = this.fileInfo.getAvailableObjectInfo(c.replace(/^\s*|\s*$/g, ""));
		}

		if (! this._constructorInfo)
		{
			if (this.object == window || this.object.constructor == Object)
			{
				this._constructorInfo = null;
			}
			else
			{
				this._constructorInfo = this.getLowestConstructorInfo(this.object);
			}
		}
	}
	return this._constructorInfo;
};
/**
 * 返回最近的构造器信息
 * @return {Object} 构造器信息
 */
$p.getLowestConstructorInfo = function(o)
{
	var omap = this.fileInfo.getAvailableObjectMap()
		, pl = []
		, n
		, s;

	for (n in omap)
	{
		s = omap[n];

		if ((s.prototype instanceof Object) && s != this.object && (o instanceof s) && o != Object)
		{
			var k = 0, n2;

			for (n2 in s.prototype)
			{
				k++;
			}

			if (pl.length)
			{
				var ok = pl[pl.length - 1].k;

				if (k < ok)
				{
					continue;
				}
				else if (k > ok)
				{
					pl.length = 0;
				}
			}
			pl.push(
			{
				n: n,
				k: k,
				o: s
			});
		}
	}

	if (pl.length == 0)
	{
		return null;
	}
	function tc()
	{
	}
	for (var i = 0, c, j, o, s; i < pl.length; i++)
	{
		c = pl[i].o;
		tc.prototype = c.prototype;
		o = new tc();

		for (j = 0; j < pl.length; j++)
		{
			s = pl[j].o;

			if (c != s && (o instanceof s))
			{
				pl.splice(j, 1);

				if (j <= i)
				{
					i--;
				}
				j--;
			}
		}
	}
	return this.fileInfo.getAvailableObjectInfo(pl[0].n);
}
$p.getDeclaredStaticInfos = function()
{
	if (! this._declaredStaticInfos)
	{
		var si = this.getStaticInfos()
			, dsi = []
			, i = 0;

		for (; i < si.length; i++)
		{
			var n = si[i]
				, o = si["__jsdoc__" + n];

			if (o.getPlace() == this)
			{
				dsi.push(n);
				dsi["__jsdoc__" + n] = o;
			}
		}
		this._declaredStaticInfos = dsi;
	}
	return this._declaredStaticInfos;
}
$p._buildStaticInfos = function()
{
	var infos = [];

	if (! this.object)
	{
		return infos;
	}
	var infomap = this.fileInfo.getObjectInfoMap();
	var ci = this.getConstructorInfo();

	if (ci && ci.object)
	{
		var pobj = ci.object.prototype;
	}
	pobj = pobj || {};

	var constr = this.getConstructorInfo();

	if (constr)
	{
		var inherits = constr.getInheritList();
	}
	else
	{
		var inherits = [];
	}

	for (var n in this.object)
	{
		var an = this.name + "." + n;
		var o = infomap[an];

		if (!o)
		{
			o = new ObjectInfo(this.fileInfo, an, this.object[n]);
		}
		var info = new MemberInfo(this, n, o, inherits, true);

		if (n in pobj)
		{
			if (pobj[n] == this.object[n])
			{
				info.attributeType = "prototype";
			}
			else
			{
				info.attributeType = "override";
			}
		}
		else
		{
			info.attributeType = "instance";
		}
		infos.push(n);
		infos["__jsdoc__" + n] = info;
	}

	var map = this.docEntry.getStaticMemberMap();

	for (var n in map)
	{
		if (!(n in this.object))
		{
			var an = this.name + "." + n;
			var o = new ObjectInfo(this.fileInfo, an, null, map[n]);
			var info = new MemberInfo(this, n, o, inherits, true);
			info.attributeType = "instance";
			infos.push(n);
			infos["__jsdoc__" + n] = info;
		}
	}

	infos.sort(function(a, b)
	{
		var av = infos["__jsdoc__" + a].memberInfo.getAccess();
		var bv = infos["__jsdoc__" + b].memberInfo.getAccess();
		return (access.indexOf(bv) - access.indexOf(av)) || (a > b ? 1 : - 1);
	});

	return infos;
}

$p.getStaticInfos = function()
{
	if (! this._staticInfos)
	{
		this._staticInfos = this._buildStaticInfos();
		for (var i = 0; i < this._staticInfos.length; i++)
		{
			if (this._staticInfos[i] == "prototype")
			{
				this._staticInfos.splice(i, 1);
				delete this._staticInfos["__jsdoc__prototype"];
			}
		}
	}
	return this._staticInfos;
}

$p.toString = function()
{
	return this.getType() + this.name;
}

$p.getAccess = function()
{
	return this.docEntry ? this.docEntry.getAccess() || "" : "";
}

var access = "private,intenal,protected,friend,public";//var types = "string,number,boolean,object,function";
function MemberInfo(obj, mn, mo, inhs, s)
{
	this.objInfo = obj;
	this.name = mn;
	this.memberInfo = mo;
	this.inherits = inhs;
	this["static"] = s;
}

MemberInfo.prototype.getPlace = function()
{
	if (! this.place)
	{
		if (this.attributeType.indexOf("instance") >= 0)
		{
			this.place = this.objInfo;
		}

		for (var i = this.inherits.length - 1; i >= 0; i--)
		{
			if (this.name in this.inherits[i].object.prototype)
			{
				this.place = this.inherits[i];
			}
		}

		if (! this.place)
		{
			this.place = this.objInfo;
		}
	}
	return this.place;
}

function UnknowInfo(fileInfo, name, obj, doc)
{
	this.fileInfo = fileInfo;
	this.name = name;
	this.object = obj;
	this.docEntry = doc || emptyDocEntry;
}
/**
 * @Type.extend ObjectInfo
 */
$p = (Type.extend(UnknowInfo.prototype, ObjectInfo.prototype, false));

$p.getConstructorInfo = function()
{
	return null;
};
/**
 * @Type.extend ObjectInfo
 */
function PrimitiveInfo(fileInfo, name, obj, doc)
{
	this.fileInfo = fileInfo;
	this.name = name;
	this.object = obj;
	this.docEntry = doc;
}
$p = (Type.extend(PrimitiveInfo.prototype, ObjectInfo.prototype, false));

$p.getConstructorInfo = function()
{
	return null;
};
/**
 * @extend ObjectInfo
 */
function FunctionInfo(fileInfo, name, obj, doc)
{
	this.fileInfo = fileInfo;
	this.name = name;
	this.object = obj;
	this.docEntry = doc;
}

$p = (Type.extend(FunctionInfo, ObjectInfo, false)).prototype;

$p.getStaticInfos = function()
{
	if (! this._staticInfos)
	{
		this._staticInfos = this._buildStaticInfos();

		for (var i = 0; i < this._staticInfos.length; i++)
		{
			if (this._staticInfos[i] == "prototype")
			{
				this._staticInfos.splice(i, 1);
				delete this._staticInfos["__jsdoc__prototype"];
			}
		}
	}
	return this._staticInfos;
}

$p.getConstructorInfo = function()
{
	return functionInfo;
};

$p.getParams = function()
{
	if (this._params)
	{
		return this._params;
	}
	var params = this.docEntry.getParams();

	if (! params)
	{
		if (this.object instanceof Function)
		{
			try
			{
				params = /\(?:([^\(\)]*)\)/.exec(this.object.toString())[1].split(/\s*,\s*/);
			}
			catch (e)
			{
				params = [];
			}
		}
		else
		{
			params = [];
		}
	}
	var ps = [];

	if (params.length)
	{
		for (var i = 0; i < params.length; i++)
		{
			if (params[i])
			{
				ps.push(new TypeInfomation(params[i]));
			}
		}
	}
	return (this._params = ps);
}

$p.getArguments = function()
{
	return this.docEntry.getArguments();
}

$p.getReturnInfo = function()
{
	if (! this._returnInfo)
	{
		var r = this.docEntry.getReturn();
		r = new TypeInfomation(r);
		r.type = this.docEntry.getReturnType() || r.type || "void";

		r.description = (r.name || "") + " " + (r.description || "");
		this._returnInfo = r;
	}
	return this._returnInfo;
}

/**
 * eg:{ClassA} ca infomation
 * @private
 */
function TypeInfomation(text)
{
	if(System.isSafari())//有中文时正则解析失败
		text = escape(text).replace(/%20/g, " ").replace(/%7B/g, "{").replace(/%7D/g, "}").replace(/%5B/g, "[").replace(/%5D/g, "]").replace(/%7C/g, "|").replace(/%3D/g, "=");
	var m = /^\s*([\w\$]+)?\s*(\{|<)/.exec(text);
	if (m)
	{
		i = m[0].length;
		var ch = m[0][i - 1] || m[i - 1] ,j;
		if (ch == "{")
		{
			j = this.searchEnd(text, i, "{", "}");
		}
		else
		{
			j = this.searchEnd(text, i, "<", ">");
		}

		if (j)
		{
			this.type = text.substring(i, j);

			if (m[1])
			{
				this.name = m[1];
				this.description = text.substr(j + 1);
				return;
			}
			else
			{
				text = text.substr(j + 1);
			}
		}
	}

	if (! this.name)
	{
		//å¢å å¯éåæ°åçæ¯æ
		m = /^\s*?([\[?\w\$=?\]?]+)(?:\s+|$)([\s\S]*)$/.exec(text);
		//m = /^\s*([\w\$]+)(?:\s+|$)([\s\S]*)+$/.exec(text);

		if (m)
		{

			this.name = m[1]; // || dn;
			this.description = unescape(m[2]);
			/*m = (/\[([\w\$]+)(?:=?)([\w\$]*)\]/.exec(this.name));

			if (m)
			{
				this.name = "[" + m[1] + "]";
				this.optional = true;

				if (m[2]) //å¢å å¯¹é»è®¤åæ°çæ¯æ
					this.defaultValue = m[2];
			}*/
		}
		else
		{
			this.description = text;
		}
	}
}
TypeInfomation.prototype.searchEnd = function(text, i, b, e)
{
	var d = 0;

	while (++ i < text.length)
	{
		switch (text.charAt(i))
		{
			case b:
				d++;
				break;

			case e:
				if (d == 0)
				{
					return i;
				}
				else if (d < 0)
				{
					return null;
				}
				d--;
		}
	}
	return null;
};

Type.extend(FunctionInfo.prototype, ObjectInfo.prototype, false);
/**
 * @extend FunctionInfo
 */
function ConstructorInfo(fileInfo, name, obj, doc)
{
	this.fileInfo = fileInfo;
	this.name = name;
	this.object = obj;
	this.docEntry = doc;
}

$p = ConstructorInfo.prototype;

$p.findSuperInfo = function()
{
	var omap = this.fileInfo.getAvailableObjectMap();
	var tc = function()
	{
	};

	tc.prototype = this.object.prototype;
	var o = new tc();
	return this.getLowestConstructorInfo(o);
}
$p.getSuperInfo = function()
{
	if (!("_superInfo" in this))
	{
		var s = this.docEntry.getExtend();

		if (s)
		{
			this._superInfo = this.fileInfo.getAvailableObjectInfo(s);
		}

		if (this._superInfo)
		{
			return this._superInfo;
		}
		else if (this.object instanceof Function)
		{
			var p = this.object.prototype;
			var sub = false;

			for (var n in p)
			{
				if (n == "constructor")
				{
					sub = true; //(p[n] instanceof Function);
				}
			}

			if (sub || p instanceof Object && p.constructor != this.object)
			{
				this._superInfo = this.findSuperInfo();
			}
			else
			{
				this._superInfo = null;
			}
		}
		else
		{
			this._superInfo = null;
		}
	}
	return this._superInfo;
}
$p.getDeclaredInstanceInfos = function()
{
	if (! this._declaredInstanceInfos)
	{
		var ii = this.getInstanceInfos();
		var dii = [];

		for (var i = 0; i < ii.length; i++)
		{
			var n = ii[i];
			var o = ii["__jsdoc__" + n];

			if (o.getPlace() == this)
			{
				dii.push(n);
				dii["__jsdoc__" + n] = o;
			}
		}
		this._declaredInstanceInfos = dii;
	}
	return this._declaredInstanceInfos;
}
$p.getInheritList = function()
{
	if (! this._inheritList)
	{
		var cs = [];
		var c = this;

		do
		{
			cs.push(c);
		} while (c = c.getSuperInfo())
		this._inheritList = cs.reverse();
	}
	return this._inheritList;
}
$p.getStaticInfos = function()
{
	if (! this._staticInfos)
	{
		var staticInfos = this._buildStaticInfos();

		for (var i = staticInfos.length - 1; i >= 0; i--)
		{
			var n = staticInfos[i];

			if (n == "prototype")
			{
				staticInfos.splice(i, 1);
				delete staticInfos["__jsdoc__" + n];
				i--;
			}
		}
		this._staticInfos = staticInfos;
	}
	return this._staticInfos;
}
$p.getInstanceInfos = function()
{
	if (! this._instanceInfos)
	{
		var _instanceInfos = [];

		if (! this.object)
		{
			return this._instanceInfos = _instanceInfos;
		}
		var infomap = this.fileInfo.getObjectInfoMap();
		var proto = this.object.prototype;
		var pobj = this.getSuperInfo();
		pobj = pobj ? pobj.object.prototype : {};

		var inherits = this.getInheritList();
		//proto["toString"]=proto["toString"];
		//proto["valueOf"]=proto["valueOf"];

		for (var n in proto)
		{
			var an = this.name + ".prototype." + n;
			var o = infomap[an];

			if (! o)
			{
				o = new ObjectInfo(this.fileInfo, an, proto[n]);
			}
			var info = new MemberInfo(this, n, o, inherits, false);
			var t = this.docEntry.getAttributeType();

			if (t)
			{
				info.attributeType = t;
			}
			else
			{
				if (n in pobj)
				{
					if (pobj[n] == this.object[n])
					{
						info.attributeType = "inherited";
					}
					else
					{
						info.attributeType = o.docEntry.instanceAttribute ? "instance override" : "prototype override";
					}
				}
				else
				{
					info.attributeType = o.docEntry.instanceAttribute ? "instance" : "prototype";
				}
			}
			_instanceInfos.push(n);
			_instanceInfos["__jsdoc__" + n] = info;
		}
		var map = this.docEntry.getInstanceMemberMap(), nat;

		for (var n in map)
		{	nat = n == "toString";
			if (!(n in proto) || nat)
			{
				var an = this.name + ".prototype." + n;
				var o = new ObjectInfo(this.fileInfo, an, null, map[n]);
				var info = new MemberInfo(this, n, o, inherits, false);
				info.attributeType = nat ? "override" : "instance";
				_instanceInfos.push(n);
				_instanceInfos["__jsdoc__" + n] = info;
			}
		}
		_instanceInfos.sort(function(a, b)
		{
			//$log.debug(a + b + infos[a].objInfo.docEntry + infos[b].objInfo.docEntry);
			var av = _instanceInfos["__jsdoc__" + a].memberInfo.getAccess();
			var bv = _instanceInfos["__jsdoc__" + b].memberInfo.getAccess();
			return (access.indexOf(bv) - access.indexOf(av)) || (a > b ? 1 : - 1);
		});
		this._instanceInfos = _instanceInfos;
	}
	return this._instanceInfos;
}

$p.getType = function()
{
	return "constructor";
}

Type.extend(ConstructorInfo.prototype, FunctionInfo.prototype, false);

var emptyDocEntry = new function()
{
	for (var n in DocEntry.prototype)
	{
		this[n] = DocEntry.prototype[n];
	}
	this.tagAttributes =
	{
		"constructor": null
	};

	this.sourceAttributes =
	{
		"constructor": null
	};

	this.getStaticMemberMap = this.getInstanceMemberMap = function()
	{
		return {};
	};
};

var functionInfo = new function()
{
	for (var n in ConstructorInfo.prototype)
	{
		this[n] = ConstructorInfo.prototype[n];
	}
	this.object = Function;
	this.docEntry = emptyDocEntry;
};

$p = null;